package cn.ticsmyc.tools.multiThread.activeObject;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * @author Ticsmyc
 * @date 2021-06-02 21:49
 */
public abstract class ActiveObjectProxy {

    /**
     * @param interf    基于这个接口创建代理对象
     * @param servant   被代理的对象（需要实现interf接口）
     * @param scheduler 执行器
     * @return
     */
    public static <T> T newInstance(Class<T> interf, Object servant, ExecutorService scheduler) {

        @SuppressWarnings("unchecked")
        T t = (T) Proxy.newProxyInstance(interf.getClassLoader(),
                new Class[]{interf},
                new DispatchInvocationHandler(servant, scheduler));
        return t;
    }

}

class DispatchInvocationHandler implements InvocationHandler {

    private final Object delegate;
    private final ExecutorService executorService;

    public DispatchInvocationHandler(Object delegate, ExecutorService executorService) {
        this.delegate = delegate;
        this.executorService = executorService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = null;
        //如果调用的方法的返回值是Future 则是异步方法，提交任务给执行器执行
        //否则直接同步执行
        if(Future.class.isAssignableFrom(method.getReturnType())) {
            Method delegateMethod = delegate.getClass().getMethod(
                    getDelegateMethodName(method, args),
                    method.getParameterTypes());
            Future<Object> future = executorService.submit(() -> {
                Object res = null;
                try {
                    res = delegateMethod.invoke(delegate, args);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
                return res;
            });
            System.out.println("异步执行" + method.getName());
            ret = future;
        } else {
            System.out.println("同步执行" + method.getName());
            ret = method.invoke(delegate, args);
        }
        return ret;
    }

    /**
     * 根据异步方法名称，得到同步方法名称
     * （转换规则自己提前规定, 这里是  ”do+大驼峰异步方法名“）
     *
     * @param method
     * @param args
     * @return
     */
    private String getDelegateMethodName(Method method, Object[] args) {
        String name = method.getName();
        name = "do" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
        return name;
    }
}
